package com.ncf.jucai.mybatis.dao;

import com.google.common.reflect.TypeToken;
import com.ncf.jucai.common.entities.UUIDEntity;
import com.ncf.jucai.model.criteria.PageInfo;
import com.ncf.jucai.model.misc.PagedResult;
import lombok.Data;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;

@Data
public abstract class BaseDao<Entity, Mapper, Example> implements IBaseDao<Entity, Mapper, Example> {

    @Autowired
    protected Mapper mapper;

    protected Example newExample() {
        Type type = new TypeToken<Example>() {
        }.getType();
        try {
            return (Example) type.getClass().newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected Entity newEntity() {
        Type type = new TypeToken<Entity>() {
        }.getType();
        try {
            return (Entity) type.getClass().newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Mapper getMapper() {
        return this.mapper;
    }

    @Override
    public Entity select(Object pk) {
        return (Entity) invokeMethodWithoutException(getMapper(), "selectByPrimaryKey", pk);
    }

    @Override
    public int insert(Entity entity) {
        if (entity instanceof UUIDEntity) {
            ((UUIDEntity) entity).setId(UUID.randomUUID().toString());
        }
        return (int) invokeMethodWithoutException(getMapper(), "insertSelective", entity);
    }

    @Override
    public int update(Entity entity) {
        return (int) invokeMethodWithoutException(getMapper(), "updateByPrimaryKeySelective", entity);
    }

//    @Override
//    public Entity updateSelective(Entity entity){
//        return (Entity) invokeMethodWithoutException(getMapper(), "updateByPrimaryKeySelective", entity);
//    }

    @Override
    public int delete(Object pk) {
        return (int) invokeMethodWithoutException(getMapper(), "deleteByPrimaryKey", pk);
    }

    protected int appendPageInfoIntoExample(Example example, PageInfo pageInfo) {
        try {
            PropertyUtils.setSimpleProperty(example, "offset", pageInfo.getOffset());
            PropertyUtils.setSimpleProperty(example, "size", pageInfo.getSize());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return 0;
    }

    @Override
    public List<Entity> selectByExample(Example example) {
        try {
            List list = (List) MethodUtils.invokeMethod(mapper, "selectByExample", example);
            return list;
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public PagedResult<Entity> selectByExample(Example example, PageInfo pageInfo) {
        appendPageInfoIntoExample(example, pageInfo);
        try {
            List list = (List) MethodUtils.invokeMethod(mapper, "selectByExample", example);
            PagedResult<Entity> result = new PagedResult<>();
            result.setResults(list);
            result.setTotalSize((Integer) PropertyUtils.getSimpleProperty(example, "totalCount"));
            return result;
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    private Object invokeMethodWithoutException(Object bean, String method, Object params) {
        try {
            return MethodUtils.invokeMethod(bean, method, params);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int updateByExampleSelective(Entity record, Example example) {
        try {
            return (int) MethodUtils.invokeMethod(mapper, "updateByExampleSelective", example);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int updateByExample(Entity record, Example example) {
        try {
            return (int) MethodUtils.invokeMethod(mapper, "updateByExample", example);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}
